from odoo import models, fields, api, _
from odoo.exceptions import UserError, ValidationError
import re

COMPANY = [('w', 'W'), ('j', 'J')]


class ProductTemplate(models.Model):
    _inherit = 'product.template'
    _order = "priority desc, name"

    product_template_coding_attribute_ids = fields.One2many('product.template.coding.attribute', 'product_template_id',
                                                            string="Coding Attribute Selection List", readonly=True)
    product_coding_id = fields.Many2one('product.coding.template', string="Coding Template", readonly=True)
    # Record the current category for filtering onchange categ_id itself
    previous_categ_id = fields.Many2one('product.category')
    # Whether the coding attribute value is fully selected
    default_code = fields.Char(index=True, copy=False, tracking=True,
                               string='Material Coding', compute='_compute_default_code',
                               inverse='_set_default_code', store=True, readonly=False)
    before_approval_code = fields.Char(string="Material Coding (Temporary)")
    supplier_code = fields.Char(string="Supplier Coding", store=True)
    coding_provider_id = fields.Many2one('res.partner', string="Coding Provider", domain="[('supplier_rank', '>', 0)]")
    fu_categ_name = fields.Char(related="categ_id.parent_id.name", store=True)

    # categ_code_rule_type = fields.Selection(related='categ_id.code_rule_type')

    # company = fields.Selection(COMPANY, string="Company")
    # tier = fields.Char(string="Tier", default="00")
    # structure = fields.Char(string="Halogen")
    # serial_number = fields.Char(string="P/N serial number")
    # versions = fields.Char('versions', default="A0")
    description_chi = fields.Text('product description(chinese)', tracking=True)
    description_eng = fields.Text('product description(english)', tracking=True)
    specification_chi = fields.Char('product specification(chinese)', tracking=True)
    specification_eng = fields.Char('product specification(english)', tracking=True)
    # 规格
    standards = fields.Char('Standards')
    name = fields.Char(tracking=True)
    group_system = fields.Boolean(compute='compute_user_has_groups')
    categ_id = fields.Many2one(tracking=True)
    uom_id = fields.Many2one(tracking=True)
    uom_po_id = fields.Many2one(tracking=True)

    @api.model
    def synchronous_coding_detail(self):
        product_ids = self.env['product.template'].sudo().search([('detailed_type', '=', 'product'),
                                                                  ('default_code', '!=', False),
                                                                  ('active', '=', True)])
        for pr in product_ids:
            if pr.fu_categ_name:
                coding = self.env['product.coding.template'].sudo().search([('product_tmpl_id', '=', pr.id)])
                if not coding:
                    new_coding_id = self.env['product.coding.template'].sudo().create({
                        'supplier_choice': 'not',
                        'department_choice': 'multi',
                        'category_id': pr.categ_id.id,
                        'uom_id': pr.uom_id.id,
                        'uom_po_id': pr.uom_po_id.id,
                        'state': 'done',
                        'product_tmpl_id': pr.id
                    })
                    new_coding_id.write({
                        'name': pr.name,
                        'name_en': pr.description_eng,
                        'default_code': pr.default_code,
                    })

    def get_serial_number(self):
        for item in self:
            serial_number = self.env['ir.sequence'].get_serial_number(code='product_template.default_code', prefix='',
                                                                      padding=6)
            item.serial_number = serial_number
            return serial_number

    @api.depends('name')
    def compute_user_has_groups(self):
        has_group = self.user_has_groups('base.group_system')
        self.group_system = has_group

    def write(self, vals):
        if isinstance(vals, list):
            for rec in vals:
                self.check_group_fields(rec)
        else:
            self.check_group_fields(vals)
        res = super(ProductTemplate, self).write(vals)
        product_coding_id = self.env['product.coding.template'].search([('product_tmpl_id', 'in', self.ids)], limit=1)
        if product_coding_id:
            if vals.get('name'):
                product_coding_id.write({
                    'name': vals.get('name')
                })
            if vals.get('description_eng'):
                product_coding_id.write({
                    'name_en': vals.get('description_eng')
                })
            if vals.get('default_code'):
                product_coding_id.write({
                    'default_code': vals.get('default_code')
                })
            if vals.get('categ_id'):
                product_coding_id.write({
                    'category_id': vals.get('categ_id')
                })
                product_coding_id.write({
                    'name': self.name,
                    'name_en': self.description_eng,
                    'default_code': self.default_code
                })
        return res

    def check_group_fields(self, vals):
        if 'description_eng' in vals and self.description_eng:
            if not self.env.user.has_group('base.group_system'):
                raise UserError(_('Non-administrators change the English description of products'))
        if 'description_chi' in vals and self.description_chi:
            if not self.env.user.has_group('base.group_system'):
                raise UserError(_('Non-administrators cannot change the Chinese description of the product'))
        if 'name' in vals and self.name != 'new':
            if not self.env.user.has_group('base.group_system'):
                raise UserError(_('Non-administrators cannot change the Chinese description of the product'))
        if 'default_code' in vals and self.default_code:
            if not self.env.user.has_group('base.group_system'):
                raise UserError(_('Non-administrators cannot change the default code of the product'))


class ProductTemplateCodingAttribute(models.Model):
    _name = 'product.template.coding.attribute'
    _description = 'Material Coding Attribute Selection List'
    _order = 'sequence'

    sb_material_coding_attribute_id = fields.Many2one('sb.material.coding.attribute', string="Coding Attribute",
                                                      ondelete="cascade")
    code_at = fields.Char(related="sb_material_coding_attribute_id.code")
    name = fields.Char(related="sb_material_coding_attribute_id.name", string="Attribute Name")
    sequence = fields.Integer(string='Sequence')
    # 参与描述
    coding_description = fields.Boolean(string="Coding Description", default=False)
    # 参与编码
    coding_participation = fields.Boolean(string="Coding Participation", default=False)
    material_coding_attribute_value_id = fields.Many2one('sb.material.coding.attribute.value', string="Attribute Value",
                                                         domain="[('material_coding_attribute_id', '=', sb_material_coding_attribute_id)]",
                                                         ondelete="restrict")
    material_coding_attribute_value_code = fields.Char(string="Attribute Code", compute="_compute_value_code",
                                                       store=True)

    coding_attribute_type = fields.Selection(related="sb_material_coding_attribute_id.coding_attribute_type")
    input_length = fields.Integer(related="sb_material_coding_attribute_id.input_length")
    input_value = fields.Text(string="Input Value")
    core_board_sum = fields.Char(string="Core Board Count")

    product_template_id = fields.Many2one('product.template', ondelete="cascade")
    attribute_import_id = fields.Many2one('sb.pro.attribute.import', ondelete="cascade")
    product_coding_id = fields.Many2one('product.coding.template', string="Coding Template")
    coding_provider_id = fields.Many2one('res.partner', string="Supplier", domain="[('supplier_rank', '>', 0)]")
    department_id = fields.Many2one('hr.department', string="Department", domain=[('parent_id', '!=', False)])

    # placeholder = fields.Text(string="Placeholder")

    # 计算属性编码
    @api.depends('input_value', 'coding_participation',
                 'material_coding_attribute_value_id',
                 'coding_provider_id', 'department_id')
    def _compute_value_code(self):
        for item in self:
            if not item.coding_participation:  # 不参与编码直接返回
                item.material_coding_attribute_value_code = False
                continue
            if item.coding_attribute_type == 'value':
                item.material_coding_attribute_value_code = item.material_coding_attribute_value_id.value_code if item.material_coding_attribute_value_id else False
            elif item.coding_attribute_type == 'input':
                if item.input_value:
                    # 基材铜厚
                    # if '/' in item.input_value:
                    #     # 常规校验------------------------------------------------------------------------
                    #     if len(item.input_value) > item.input_length:
                    #         item.material_coding_attribute_value_code = False
                    #         raise UserError(_('Input value length overrun' + str(self.input_length)))
                    #     if not re.match(r'^[A-Z0-9/]+$', item.input_value):
                    #         raise UserError(_('Input values can only be numeric or/and uppercase alphabets'))
                    #     result = item.input_value.split('/')
                    #     if len(result) != 2:
                    #         raise UserError(
                    #             _('Substrate copper thickness input value is not standardized：' + item.input_value))
                    #     # 头大尾小校验------------------------------------------------------------------------
                    #     head_v = result[0]
                    #     tail_v = result[1]
                    #     # 替代字母数组
                    #     custom_array = ['E', 'Q', 'T', 'J', 'H', 'M', 'A', 'B', 'C', 'D', 'U', 'F', 'G', 'V', 'K', 'L',
                    #                     'N', 'W', 'P', 'X', 'R', 'S', 'Y']
                    #     # 替代数字数组
                    #     custom_sum_array = [0.1, 0.2, 0.3, 0.4, 0.5, 0.7, 10, 11, 12, 13, 14, 15, 16, 17, 20, 21,
                    #                         23, 24, 25, 26, 27, 28, 29]
                    #     if not head_v.isdigit():
                    #         if head_v in custom_array:
                    #             sum_head_v = custom_sum_array[custom_array.index(head_v)]
                    #         else:
                    #             raise UserError(
                    #                 _('Substrate copper thickness input value is not standardized：' + head_v))
                    #     else:
                    #         sum_head_v = int(head_v)
                    #     if not tail_v.isdigit():
                    #         if tail_v in custom_array:
                    #             sum_tail_v = custom_sum_array[custom_array.index(tail_v)]
                    #         else:
                    #             raise UserError(
                    #                 _('Substrate copper thickness input value is not standardized：' + tail_v))
                    #     else:
                    #         sum_tail_v = int(tail_v)
                    #
                    #     if sum_head_v > sum_tail_v:
                    #         raise UserError(
                    #             _('The copper thickness attribute of the substrate does not allow for a larger head and a smaller tail.：' + item.input_value))
                    #     # 转换为属性编码 -------------------------------------------------------------------------
                    #     item.material_coding_attribute_value_code = head_v + tail_v
                    #
                    # else:
                    # pnl尺寸输入
                    if 'x' in item.input_value:
                        # 常规校验------------------------------------------------------------------------
                        if len(item.input_value) > item.input_length:
                            item.material_coding_attribute_value_code = False
                            raise UserError(_('Input value length overrun' + str(self.input_length)))
                        if not re.match(r'^[0-9x]+$', item.input_value):
                            raise UserError(_('Input values can only be numeric or x '))
                        result = item.input_value.split('x')
                        if len(result) != 2:
                            raise UserError(
                                _('input value is not standardized：' + item.input_value))
                        head_v = result[0]
                        tail_v = result[1]
                        # 长度不够用0补齐
                        if len(head_v) < 4:
                            head_v = '0' * (4 - len(head_v)) + head_v
                        if len(tail_v) < 4:
                            tail_v = '0' * (4 - len(tail_v)) + tail_v
                        item.material_coding_attribute_value_code = head_v + tail_v
                    # 含胶量
                    elif item.sb_material_coding_attribute_id.code == "HJL":
                        if len(item.input_value) > item.input_length:
                            item.material_coding_attribute_value_code = False
                            raise UserError(
                                _('The length of the input value exceeds the limit.' + str(self.input_length)))
                        if not re.match(r'^[0-9.]*$', item.input_value):
                            raise UserError(_('The input value can only be numerical..'))
                        # 长度不够用0补齐
                        input_value = item.input_value
                        if len(input_value) < 3:
                            input_value = input_value + '0' * (3 - len(input_value))
                        item.material_coding_attribute_value_code = (''.join(re.findall(r'\d+', input_value)))
                    else:
                        # 普通数字输入----------------------------------------------------------------------------
                        if len(item.input_value) > item.input_length:
                            item.material_coding_attribute_value_code = False
                            raise UserError(
                                _('The length of the input value exceeds the limit.' + str(self.input_length)))
                        if not re.match(r'^[0-9.]*$', item.input_value):
                            raise UserError(_('The input value can only be numerical..'))
                        # 长度不够用0补齐
                        input_value = item.input_value
                        if len(input_value) < item.input_length:
                            input_value = '0' * (item.input_length - len(input_value)) + input_value
                        # 玻璃布种
                        if item.sb_material_coding_attribute_id.code == "BLBZ":
                            item.material_coding_attribute_value_code = 'P' + (''.join(re.findall(r'\d+', input_value)))
                        else:
                            item.material_coding_attribute_value_code = (''.join(re.findall(r'\d+', input_value)))
                else:
                    item.material_coding_attribute_value_code = False
            # elif item.coding_attribute_type == 'partner':
            #     c = item.coding_provider_id.code
            #     item.material_coding_attribute_value_code = c or 'ZZZZZ'
            # 芯板结构---------------------------------------------------------------------------------
            # elif item.coding_attribute_type == 'plate_structure':
            #     print(1)
            # if item.input_value:
            #     if not re.match(r'^[0-9x+]*$', item.input_value):
            #         raise UserError(
            #             _('The input value can only consist of numbers, asterisks (x), or plus signs (+).'))
            #     core_boards = item.input_value.split('+')
            #     core_board_sum = 0
            #     for core_board in core_boards:
            #         core_board_split = core_board.split('x')
            #         if len(core_board_split) != 2 or not core_board_split[1]:
            #             raise UserError(
            #                 _('The input value for the core board structure is not standardized.：' + core_board))
            #         if len(core_board_split[0]) != 4 or not core_board_split[0].isdigit():
            #             raise UserError(
            #                 _('There must be a 4-digit number immediately preceding the asterisk (x).' + core_board))
            #         cloth_num = self.env['sb.glass.cloth.type.list'].search([('name', '=', core_board_split[0])], limit=1)
            #         if not cloth_num:
            #             raise UserError(
            #                 _('The type of glass cloth is illegal' + core_board_split[0]))
            #         core_board_sum += int(core_board_split[1])
            #     # 计算芯板张数----------------------------------------------------------------------
            #     if 9 < core_board_sum < 33:
            #         # 芯板PP张数超9数组
            #         core_board_sum_c9 = ['A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'J', 'K', 'L', 'M', 'N',
            #                              'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y']
            #         item.core_board_sum = core_board_sum_c9[core_board_sum - 10]
            #     elif core_board_sum > 32:
            #         raise UserError(_('The number of core board sheets exceeds 32.'))
            #     else:
            #         item.core_board_sum = str(core_board_sum)
            # else:
            #     item.material_coding_attribute_value_code = '0A'
            #     item.core_board_sum = '0'
            elif item.coding_attribute_type == 'flow_number':
                co = f"{item.product_coding_id.category_id.flow_number:0{item.input_length}}"
                if item.product_template_id:
                    co = f"{item.product_template_id.categ_id.flow_number:0{item.input_length}}"
                if item.product_coding_id and item.product_coding_id.state != 'done':
                    co = '#####'
                item.material_coding_attribute_value_code = co
            # elif item.coding_attribute_type == 'department':
            #     d = item.department_id.code
            #     item.material_coding_attribute_value_code = d or 'ZZZ'
            else:
                item.material_coding_attribute_value_code = False

            # 计算芯板结构----------------------------------------------------------------------
            # print(item.name)
            coding_ids = item.product_coding_id.product_coding_attribute_ids
            # if item.product_template_id:
            #     coding_ids = item.product_template_id.product_template_coding_attribute_ids
            input_val = plate_coding_id = False
            for coding_id in coding_ids:
                if coding_id.coding_attribute_type == 'plate_structure':
                    input_val = coding_id.input_value
                    plate_coding_id = coding_id

                    if coding_id.input_value:
                        if not re.match(r'^[0-9x+]*$', coding_id.input_value):
                            raise UserError(
                                _('The input value can only consist of numbers, asterisks (x), or plus signs (+).'))
                        core_boards = coding_id.input_value.split('+')
                        core_board_sum = 0
                        for core_board in core_boards:
                            core_board_split = core_board.split('x')
                            if len(core_board_split) != 2 or not core_board_split[1]:
                                raise UserError(
                                    _('The input value for the core board structure is not standardized.：' + core_board))
                            if len(core_board_split[0]) != 4 or not core_board_split[0].isdigit():
                                raise UserError(
                                    _('There must be a 4-digit number immediately preceding the asterisk (x).' + core_board))
                            cloth_num = self.env['sb.glass.cloth.type.list'].search(
                                [('name', '=', core_board_split[0])], limit=1)
                            if not cloth_num:
                                raise UserError(
                                    _('The type of glass cloth is illegal' + core_board_split[0]))
                            core_board_sum += int(core_board_split[1])
                        # 计算芯板张数----------------------------------------------------------------------
                        if 9 < core_board_sum < 33:
                            # 芯板PP张数超9数组
                            core_board_sum_c9 = ['A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'J', 'K', 'L', 'M', 'N',
                                                 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y']
                            coding_id.core_board_sum = core_board_sum_c9[core_board_sum - 10]
                        elif core_board_sum > 32:
                            raise UserError(_('The number of core board sheets exceeds 32.'))
                        else:
                            coding_id.core_board_sum = str(core_board_sum)
                    else:
                        coding_id.material_coding_attribute_value_code = '0A'
                        coding_id.core_board_sum = '0'

            if input_val and plate_coding_id:
                sum_coding_ids = coding_ids.ids
                # 找到当前对象在 ids 中的位置
                try:
                    try:
                        current_index = sum_coding_ids.index(plate_coding_id.id)
                    except:
                        current_index = sum_coding_ids.index(plate_coding_id.id.origin)
                except:
                    raise UserError(
                        _('Please click the Save button in the upper left corner to save the relevant content, and then enter the core board structure field'))
                # 如果当前对象不是第一个对象
                if current_index > 0:
                    # 获取在当前位置之前的所有对象
                    previous_ids = sum_coding_ids[:current_index]
                    previous_code = plate_coding_id.core_board_sum if plate_coding_id.core_board_sum else '0'
                    for previous_id in previous_ids:
                        attribute = self.env['product.template.coding.attribute'].search([
                            ('id', '=', previous_id)
                        ])
                        if attribute[0].material_coding_attribute_value_code:
                            previous_code += attribute[0].material_coding_attribute_value_code
                    plate_structure_code_id = self.env['plate.structure.code'].search([
                        ('first_code', '=', previous_code),
                        ('tail_code', '=', input_val)
                    ], limit=1)
                    # 如果已存在，直接获取相应编码
                    if plate_structure_code_id:
                        plate_structure_code = plate_structure_code_id.code
                    else:
                        # 未找到，向下递增A-Z
                        plate_structure_firsts = self.env['plate.structure.code'].search([
                            ('first_code', '=', previous_code)
                        ])
                        f_num = len(plate_structure_firsts) if plate_structure_firsts else 0
                        plate_structure_code = chr(f_num + 65)
                        self.env['plate.structure.code'].create({
                            'first_code': previous_code,
                            'tail_code': input_val,
                            'code': plate_structure_code
                        })
                    plate_coding_id.material_coding_attribute_value_code = plate_coding_id.core_board_sum + plate_structure_code

    @api.onchange('coding_provider_id')
    def _onchange_coding_provider_id(self):
        if self.coding_provider_id:
            self.product_coding_id.coding_provider_id = self.coding_provider_id
        else:
            self.product_coding_id = False


class PlateStructureCode(models.Model):
    _name = 'plate.structure.code'
    _description = 'Core Board Structure Attribute Coding Helper Class'

    # Previous attribute coding record of the core board structure
    first_code = fields.Char(string="First Code")
    # Tail coding record of the core board structure itself, e.g.: 1080*1024
    tail_code = fields.Char(string="Tail Code")
    # The unique attribute coding based on the combination of the previous two codes, e.g.: A
    code = fields.Char(string="Attribute Code")
